package org.trinity.netdisk.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.trinity.netdisk.entity.*;
import org.trinity.netdisk.enums.ResultCode;
import org.trinity.netdisk.enums.ShareDays;
import org.trinity.netdisk.exception.file.FileNotExistException;
import org.trinity.netdisk.exception.file.OperationFailedException;
import org.trinity.netdisk.exception.shareFile.InvalidCheckCodeException;
import org.trinity.netdisk.exception.shareFile.ShareFileExpireException;
import org.trinity.netdisk.mapper.*;
import org.trinity.netdisk.service.IFileService;
import org.trinity.netdisk.service.IShareFileService;
import org.trinity.netdisk.util.*;
import org.trinity.netdisk.vo.shareFile.ShareFileDetailInfoVO;
import org.trinity.netdisk.vo.shareFile.ShareFileLinkedCodeVO;
import org.trinity.netdisk.vo.shareFile.ShareFileVO;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@SuppressWarnings("unused")
@Service
@Transactional
public class ShareFileServiceImpl implements IShareFileService {
    @Autowired
    UserFileMapper userFileMapper;

    @Autowired
    ShareFileMapper shareFileMapper;

    @Autowired
    ShareURLMapper shareURLMapper;

    @Value("${shareUrl}")
    String shareUrl;

    @Autowired
    UserMapper userMapper;

    @Autowired
    FileMapper fileMapper;

    @Autowired
    IFileService fileService;

    private final Long millisecondsPerDay = 86400000L;

    private final Long downloadTokenExpireTime = 24 * 60 * 60 * 1000L;

    /**
     * 获取分享链接
     *
     * @param userId     用户id
     * @param userFileId 用户文件表id
     * @param shareDays  分享天数
     * @return
     */
    @Override
    public ShareFileLinkedCodeVO getShareFileUrl(Long userId, Long userFileId, ShareDays shareDays) {
        checkShareFileEx(userId, userFileId);

        ShareFile shareFile = new ShareFile();
        String checkCode = ShareFileCodeBuilder.getCheckCode();
        shareFile.setShareDays(shareDays);
        shareFile.setShareUserId(userId);
        shareFile.setShareUserFileId(userFileId);
        shareFile.setCheckNum(checkCode);
        shareFileMapper.insert(shareFile);

        Long id = shareFile.getId();
        ShareFileLinkedCodeVO shareFileLinkedCodeVO = new ShareFileLinkedCodeVO();
        shareFileLinkedCodeVO.setUrl(shareUrl + BaseParseUtil.getShortUrl(id));
        shareFileLinkedCodeVO.setCheckCode(checkCode);

        ShareURL shareURL = new ShareURL();
        shareURL.setShareId(id);
        shareURL.setUrl(shareFileLinkedCodeVO.getUrl());
        shareURLMapper.insert(shareURL);

        return shareFileLinkedCodeVO;
    }

    /**
     * 根据url 解析文件信息
     *
     * @param shareFileId
     * @return
     */
    @Override
    public ResponseWrapper getShareFileDetailInfo(String shareFileId) {
        Long id = BaseParseUtil.parseUrl(shareFileId);
        ShareFile shareFile = shareFileMapper.selectByPrimaryKey(id);
        if (shareFile == null) {
            throw new FileNotExistException();
        }
        if (!checkShareTime(id)) {
            throw new ShareFileExpireException();
        }
        ShareFileDetailInfoVO shareFileDetailInfoVO = new ShareFileDetailInfoVO();
        UserFile userFile = userFileMapper.findUserFileById(shareFile.getShareUserFileId());
        File file = fileMapper.selectFileByFileId(userFile.getFileId());
        User user = userMapper.selectById(userFile.getUserId());
        //构造前端返回对象
        shareFileDetailInfoVO.setShareFileName(userFile.getName());
        shareFileDetailInfoVO.setShareFileSize(FileSizeFormatUtil.fileSizeFormat(Float.valueOf(file.getSize())));
        shareFileDetailInfoVO.setShareUserName(user.getUserName());
        shareFileDetailInfoVO.setShareTime(shareFile.getShareDays());
        shareFileDetailInfoVO.setShareFileTime(DateUtil.dateFormat(shareFile.getShareStartTime()));
        //构造过期时间
        if (shareFile.getShareDays() == ShareDays.PERMANENT) {
            shareFileDetailInfoVO.setExpiredTime("永久");
        } else {
            Date exDate = new Date(shareFile.getShareDays().getCode() * millisecondsPerDay + shareFile.getShareStartTime().getTime());
            shareFileDetailInfoVO.setExpiredTime(DateUtil.dateFormat(exDate));
        }
        return new ResponseWrapper(ResultCode.SUCCESS, shareFileDetailInfoVO);
    }

    /**
     * 验证提取码
     */
    @Override
    public void checkShareCode(String shareFileId, String checkCode) {
        Long id = BaseParseUtil.parseUrl(shareFileId);
        final ShareFile shareFile = shareFileMapper.selectByPrimaryKeyAndCheckCode(id, checkCode);
        if (shareFile == null) {
            throw new InvalidCheckCodeException();
        }
        if (!checkShareTime(id)) {
            throw new ShareFileExpireException();
        }
    }

    @Override
    public void download(HttpHeaders httpHeaders, HttpServletResponse response, String shareFileId, String checkCode) throws IOException {
        Long id = BaseParseUtil.parseUrl(shareFileId);
        ShareFile shareFile = shareFileMapper.selectByPrimaryKeyAndCheckCode(id, checkCode);
        if (shareFile == null) {
            throw new InvalidCheckCodeException();
        }
        if (!checkShareTime(id)) {
            throw new ShareFileExpireException();
        }
        Long shareUserId = shareFile.getShareUserId();
        Long userFileId = shareFile.getShareUserFileId();
        Integer tempUserDownloadLimit = 524288;

        User tempUser = new User();
        tempUser.setId(shareUserId);
        Role tempRole = new Role();
        tempRole.setDownloadLimit(tempUserDownloadLimit);
        tempUser.setRole(tempRole);

        String tempToken = JWTUtil.sign(tempUser, downloadTokenExpireTime);
        fileService.download(userFileId, response, httpHeaders, tempToken);
    }

    @Override
    public List<ShareFileVO> selectShareVO(Long userId) {
        return shareFileMapper.selectShareFileVOByUserId(userId);
    }

    @Override
    public void delete(Long userId, Long id) {
        ShareFile shareFile = shareFileMapper.selectByPrimaryKey(id);
        Long shareUserId = Optional.ofNullable(shareFile).map(ShareFile::getShareUserId).orElseThrow(FileNotExistException::new);
        if (!shareUserId.equals(userId)) {
            throw new OperationFailedException();
        }
        shareFileMapper.deleteByPrimaryKey(id);
    }

    /**
     * 检测用户文件是否存在
     *
     * @param userId
     * @param userFileId
     */
    private void checkShareFileEx(Long userId, Long userFileId) {
        UserFile userFileByIdAndUserId = userFileMapper.findUserFileByIdAndUserId(userFileId, userId);
        if (userFileByIdAndUserId == null) {
            throw new FileNotExistException();
        }
    }

    /**
     * 检测分享文件是否过期
     * true 未过期
     * false 过期
     *
     * @param shareFileId
     * @return
     */
    private boolean checkShareTime(Long shareFileId) {
        ShareFile shareFile = shareFileMapper.selectByPrimaryKey(shareFileId);
        ShareDays shareDays = shareFile.getShareDays();
        if (shareDays.getCode() == -1) {
            return true;
        }
        Date shareStartTime = shareFile.getShareStartTime();
        Date now = new Date();
        long time = now.getTime() - shareStartTime.getTime();
        return time < shareDays.getCode() * millisecondsPerDay;
    }
}
